#include <stdio.h>
#include <stdlib.h>

/*osip2/eXosip2 header*/
#include "eXosip2/eXosip2.h"  
#include "eXosip2/eX_call.h"
#include "eXosip2/eX_setup.h"
#include "osipparser2/osip_parser.h" 
#include "osipparser2/osip_message.h"
#include "osipparser2/osip_body.h"

#include "base64.h"
#include "md5.h"
#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"
#include "od_sip_comn.h"
#include "od_sip_event_distributor.h"
#include "od_sip_register_server.h"


#define SIP_REG_SERVER_TSK_STACK_SIZE              (2 * 1024 * 1024)

#define AUTH_TYPE       ("Digest")
#define AUTH_RELM       ("\"Suma@host.com\"")
#define AUTH_NONCE      ("\"dcd98b7102dd2f0e\"")
#define AUTH_OPAQUE     ("\"5ccc069c403ebaf9f0171e9517f40e41\"")
#define AUTH_ALGORITHM  ("\"md5\"")
#define AUTH_TRUE       ("\"true\"")
#define AUTH_FALSE      ("\"false\"")


typedef struct 
{
    TASK_COMMON_MEMBERS

    /*privite params*/
    pipeline_handle g_event_pipe;
    od_sip_ctx *ctx;
    eXosip_event_t *event; 
    od_sip_event_queue evt_que;
}od_sip_reg_server_obj;


params_def od_sip_reg_server_dynamic_params_def[]=
{
    {OFFSET(od_sip_reg_server_dynamic_params, userID),       PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {OFFSET(od_sip_reg_server_dynamic_params, passwd),       PT_VAR_STR | PT_MALLOC_FREE , sizeof(int), 0, 64},
    {OFFSET(od_sip_reg_server_dynamic_params, realm),        PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {OFFSET(od_sip_reg_server_dynamic_params, algorithm),    PT_VAR_INT32 | PT_MALLOC_FREE, sizeof(int), 0, 65535},
    {OFFSET(od_sip_reg_server_dynamic_params, nonce),        PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {0, 0, 0, 0, 0}
};


const od_sip_reg_server_dynamic_params glb_sip_reg_server_dynamic_params_default = 
{
    "340200000022200000001",   /*userID*/
    "12345678",                /*passwd*/
    "340200000",               /*realm*/
    0,                         /*algorithm*/
    "123456789"                /*nonce*/
};


/* 得到NONCE字段 */
static int get_auth_nonce(const char *server_ip,
                           char *nonce,
                           char *passwd)
{
    time_t curr_time = 0;
    char bindata[256] = {'\0'};
    char base64[256] = {'\0'};
    curr_time = time(NULL);

    sprintf((char *)bindata, "%ld:%s:%s",curr_time, server_ip, passwd);
    Base64encode(base64, bindata, strlen(bindata));
    sprintf(nonce, "\"%s\"", base64);

    return strlen(nonce);
}


/*return 401 unauthorized ANSWER*/
static int sip_reg_server_unauthorized(od_sip_reg_server_obj *obj)
{
    od_sip_reg_server_dynamic_params *params = obj->params;
    od_sip_ctx *ctx = obj->ctx;
    od_media_config cfg;
    char realm[64] = {0};
    char nonce[1024] = {0};
    osip_message_t *answer = NULL;
    osip_www_authenticate_t *header = NULL;
    int len;

    /*get local device paramster, IP addr etc.*/
    if (obj->ctx->cb.cfg_cb.get_cfg)
        obj->ctx->cb.cfg_cb.get_cfg(obj->ctx->cb.cfg_cb.h_callback, &cfg);

    /*build www_authenticate header*/
    osip_www_authenticate_init(&header);
    if (header == NULL) {
        RPTERR("osip_www_authenticate_init return error!!");
        return OD_ERROR;
    }

    /*auth_type*/
    header->auth_type = (char *)osip_malloc(strlen(AUTH_TYPE) + 1);
    strcpy(header->auth_type, AUTH_TYPE);

    /*realm*/
    snprintf(realm, sizeof(realm), "\"%s\"", cfg.ipaddr);
    header->realm = (char *)osip_malloc(strlen(realm) + 1);
    strcpy(header->realm, realm);

    /*nonce*/
    len = get_auth_nonce(cfg.ipaddr, nonce, params->passwd);
    header->nonce = (char *)osip_malloc(len + 1);
    strncpy(header->nonce, nonce, len + 1);

    /*algorithm*/
    header->algorithm = (char *)osip_malloc(strlen(AUTH_ALGORITHM) + 1);
    strcpy(header->algorithm, AUTH_ALGORITHM);

    /*stale*/
    header->stale = (char *)osip_malloc(strlen(AUTH_FALSE) + 1);
    strcpy(header->stale, AUTH_FALSE);


    /*build 401 unauthorized answer and fill with www_authenticate header!!*/
    eXosip_lock(ctx->ex_ctx);
    eXosip_message_build_answer(ctx->ex_ctx, obj->event->tid, SIP_UNAUTHORIZED, &answer);
    if (answer == NULL) {
        RPTERR("eXosip_message_build_answer return error!!");
        eXosip_unlock(ctx->ex_ctx);
        goto exit;
    }
    /*add www_authenticate header and send!*/
    osip_list_add(&answer->www_authenticates, (void *)header, answer->www_authenticates.nb_elt);
    if (eXosip_message_send_answer(ctx->ex_ctx, obj->event->tid, SIP_UNAUTHORIZED, answer) != OSIP_SUCCESS) {
        RPTERR("eXosip_message_send_answer return error!");
        eXosip_unlock(ctx->ex_ctx);
        goto exit;
    }
    eXosip_unlock(ctx->ex_ctx);

    /*double free or corruption (fasttop): 0x0000007f60000b80*/
    //osip_www_authenticate_free(header); 
    return OD_OK;

exit:
    //osip_www_authenticate_free(header); 
    return OD_ERROR;

}

static int sip_reg_server_authentication_check(osip_authorization_t *auth, 
                                                   const char *method,
                                                   od_sip_reg_server_obj *obj)
{
    od_sip_reg_server_dynamic_params *params = obj->params;
    char ha1[HASHHEXLEN+1] = {'\0'};
    char ha2[HASHHEXLEN+1] = {'\0'};
    char response[HASHHEXLEN+1] = {'\0'};
    char sip_rsp[HASHHEXLEN+1] = {'\0'};
    char *cnonce = NULL;
    char user_name[256] = {'\0'};
    char realm[256] = {'\0'};
    char nonce[256] = {'\0'};
    char uri[256] = {'\0'};
    char *pha1 = NULL;
    char *qop = NULL;
    char *nonce_count = NULL;
    int version = 0;

    /*remove "" in nonce="dea98f6f8dc0b786d685404ddf003b62" string*/
    if (auth->username)
        sscanf(auth->username, "%*c%[^\"]", user_name);

    if (auth->realm)
        sscanf(auth->realm, "%*c%[^\"]", realm);

    if (auth->nonce)
        sscanf(auth->nonce, "%*c%[^\"]", nonce);

    if (auth->uri)
        sscanf(auth->uri, "%*c%[^\"]", uri);

    if (auth->response)
        sscanf(auth->response, "%*c%[^\"]", sip_rsp);

    if (!strncasecmp(auth->algorithm, "MD5", strlen("MD5"))) {
        digest_calc_ha1(auth->algorithm,
                        user_name,
                        realm,
                        params->passwd,
                        nonce,
                        cnonce,
                        ha1);
        
        pha1 = ha1;
        version = 0;
        digest_calc_response(pha1,
                            nonce,
                            nonce_count,
                            cnonce,
                            qop,
                            version,
                            (char *)method,
                            uri,
                            ha2,
                            response);

    }

    if ((strlen(response) > 0) 
        && (!strncmp(sip_rsp, response, strlen(response)))) {
        return OD_OK;
    } else {
        RPTWRN("sip_rsp:%s, response:%s, passwd %s",
                sip_rsp, response, params->passwd);

        RPTWRN("user_name:%s, realm:%s, nonce:%s, uri:%s",
                    user_name, realm, nonce, uri);
        return OD_ERROR;
    }  
}

static int sip_reg_server_refreash(od_sip_reg_server_obj *obj)
{
    osip_authorization_t *auth = NULL;
    od_sip_ctx *ctx = obj->ctx;
    osip_header_t *header = NULL;
    int expires = 0;
    
    osip_message_get_authorization(obj->event->request, 0, &auth);
    if (auth == NULL) {
        sip_reg_server_unauthorized(obj);
        return OD_ERROR;
    }

    if (sip_reg_server_authentication_check(auth, "REGISTER", obj) != OD_OK) {
        /*means error, told client never register agin!!*/
        od_sip_send_answer_with_code(ctx->ex_ctx, obj->event, SIP_FORBIDDEN);
        return OD_ERROR;
    }

    osip_message_get_expires(obj->event->request, 0, &header);
    if (header && header->hvalue) {
        expires = atoi(header->hvalue);
    } else {
        od_sip_send_answer_with_code(ctx->ex_ctx, obj->event, SIP_INTERVAL_TOO_BRIEF);
        return OD_ERROR;
    }

    /*add Date in header filed!*/
    //TODO

    /*expiers==0 means log out!*/
    if (expires == 0) {
        //refresh database change log out status!!
    } else {
        //refreah database online status, update some data.
    }
    
    /*return confirm message 200 OK*/
    od_sip_send_answer_with_code(ctx->ex_ctx, obj->event, SIP_OK);

    return OD_OK;
}

static void sip_reg_server_account(od_sip_reg_server_obj *obj)
{
    char *username = NULL;
    eXosip_event_t *event = obj->event; 
    osip_from_t *from = event->request->from;
    osip_contact_t *contact = NULL;

    /*get username in SIP REGISTER header*/
    contact = (osip_contact_t *)osip_list_get(&event->request->contacts, 0);
    if (contact != NULL 
        && contact->url != NULL
        && contact->url->username != NULL) {
        username = contact->url->username;
    } else if (from != NULL 
        && from->url != NULL 
        && from->url->username != NULL) {
        username = from->url->username;
    } else {
        RPTERR("REGISTER header error! miss url&username!");
        return;
    }

    /*get account from database*/
    //TODO.

    RPTDBG("username %s", username);
    
    /*
    if (new REGISTER user) {
        deal new REGISTER
    } else {
        deal refrsh REGISTER
    }
    */
    /*test code*/
    {
        static int fake_db_result = 0;

        if (fake_db_result == 0) {
            if (sip_reg_server_unauthorized(obj) == OD_OK) {
                fake_db_result = 1;
                RPTDBG("");
            }
        } else {
            if (sip_reg_server_refreash(obj) == OD_OK) {
                RPTDBG("client %s register OK!!", username);
            }
        }
    }

    
}

static int sip_reg_server_open_register(od_sip_reg_server_obj *obj)
{
    od_sip_ctx *ctx = obj->ctx;
    osip_header_t *header = NULL;
    eXosip_event_t *event = obj->event; 
    osip_from_t *from = event->request->from;
    osip_contact_t *contact = NULL;
    int expires = 0;
    char *username = NULL;

    /*get username in SIP REGISTER header*/
    contact = (osip_contact_t *)osip_list_get(&event->request->contacts, 0);
    if (contact != NULL 
        && contact->url != NULL
        && contact->url->username != NULL) {
        username = contact->url->username;
    } else if (from != NULL 
        && from->url != NULL 
        && from->url->username != NULL) {
        username = from->url->username;
    } else {
        RPTERR("REGISTER header error! miss url&username!");
        return OD_ERROR;
    }
   
    osip_message_get_expires(obj->event->request, 0, &header);
    if (header && header->hvalue) {
        expires = atoi(header->hvalue);
    } else {
        od_sip_send_answer_with_code(ctx->ex_ctx, obj->event, SIP_INTERVAL_TOO_BRIEF);
        return OD_ERROR;
    }

    /*add Date in header filed!*/
    //TODO

    /*expiers==0 means logout!*/
    if (expires == 0) {
        //refresh database change logout status!!
    } else {
        //refreah database online status, update some data.
    }
    
    /*return confirm message 200 OK*/
    od_sip_send_answer_with_code(ctx->ex_ctx, obj->event, SIP_OK);

    return OD_OK;
}


static void sip_reg_server_handle_event(od_sip_reg_server_obj *obj)
{
    od_sip_reg_server_dynamic_params *params = obj->params;
    
    switch(obj->event->type)
    {
        case EXOSIP_MESSAGE_NEW:
            if (MSG_IS_REGISTER(obj->event->request)) {
                if (params->auth_flg == OD_TRUE) {
                    sip_reg_server_account(obj);
                } else {
                    sip_reg_server_open_register(obj);
                }  
            }          
            break;

        default:
            break;
    }

    eXosip_event_free(obj->event);
}

static int sip_reg_server_start_stop(od_sip_reg_server_obj *obj)
{
    static pj_thread_desc thread_desc;
    pj_thread_t *thread;
    pipeline_attrs fAttrs = pipeline_attrs_default;
    
    obj->ctx = od_sip_lib_start();
    OD_SIP_ASSERT_RETURN(obj->ctx, OD_ERROR);

    /*register PJ_SIP component to use pjsip-lib properly*/
    if (!pj_thread_is_registered()) {
        pj_bzero(thread_desc, sizeof(pj_thread_desc));
        pj_thread_register(obj->task_name, thread_desc, &thread);
    }

    /*create event pipe*/
    obj->g_event_pipe = pipeline_create(&fAttrs);
    if (!obj->g_event_pipe) {
        RPTERR("create event pipe error!");
        return OD_ERROR;
    }

    /*register to event distributor pthread to get event!!*/
    obj->evt_que.tsk_id = -1;
    obj->evt_que.obj_id = NULL;
    obj->evt_que.tsk_name = obj->task_name;
    obj->evt_que.queue_type = OD_SIP_EVT_QUEUE_TYPE_REGISTER_SERVER;
    obj->evt_que.g_event_pipe = obj->g_event_pipe;
    if (od_sip_evt_distrb_add(&obj->evt_que) != OD_OK) {
        RPTERR("add handle_event que pipe error!");
        return OD_ERROR;
    }

    return OD_OK;
}

static void sip_reg_server_start_stop_delete(od_sip_reg_server_obj *obj)
{
    od_sip_lib_stop();
    od_sip_evt_distrb_remove(&obj->evt_que);
    pipeline_delete(obj->g_event_pipe);
}

static int sip_reg_server_malloc_free(od_sip_reg_server_obj *obj)
{
    return OD_OK;
}


static void sip_reg_server_malloc_free_delete(od_sip_reg_server_obj *obj)
{
    //TODO
}

static int sip_reg_server_process(od_sip_reg_server_obj *obj)
{
    if (od_sip_get_event(obj->g_event_pipe, &obj->event) < 0) {
        od_msleep(50);
        return OD_OK;
    }

    sip_reg_server_handle_event(obj);

    return OD_OK;
}

od_sip_reg_server_handle od_sip_reg_server_create(od_sip_reg_server_static_params *static_params, 
                                                od_sip_reg_server_dynamic_params *dynamic_params)
{
    od_sip_reg_server_obj* obj = NULL;
    od_sip_reg_server_dynamic_params * params = NULL;

    OD_SIP_ASSERT_RETURN(static_params, NULL);
    OD_SIP_ASSERT_RETURN(static_params->task_name , NULL);

    /* create and initialize task object */
    obj = od_task_create((char *)static_params->task_name, sizeof(od_sip_reg_server_obj));
    OD_SIP_ASSERT_RETURN(obj, NULL);
    
    obj->event = NULL;
    obj->tab = od_sip_reg_server_dynamic_params_def;
    obj->params_nums = sizeof(od_sip_reg_server_dynamic_params_def) / (sizeof(params_def)) - 1;
    
    params = od_calloc(1, sizeof(od_sip_reg_server_dynamic_params));
    OD_SIP_ASSERT_RETURN(params, NULL);

    if(NULL == dynamic_params)
        memcpy(params, &glb_sip_reg_server_dynamic_params_default, sizeof(od_sip_reg_server_dynamic_params));
    else
        memcpy(params, dynamic_params, sizeof(od_sip_reg_server_dynamic_params));

    obj->params = (void *)params;
    obj->stack_size = SIP_REG_SERVER_TSK_STACK_SIZE;
    obj->pf_start_stop  = (void *)sip_reg_server_start_stop;
    obj->pf_malloc_free = (void *)sip_reg_server_malloc_free;
    obj->pf_rt_set = NULL;
    obj->pf_process     = (void *)sip_reg_server_process;
    obj->pf_malloc_free_delete = (void *)sip_reg_server_malloc_free_delete;
    obj->pf_start_stop_delete = (void *)sip_reg_server_start_stop_delete;

    RPTDBG("create success");
    return (od_sip_reg_server_handle)obj;

}


/*_*/

